package com.example.AiLangLearn.image_understanding;

import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.example.AiLangLearn.R;
import com.iflytek.sparkchain.core.LLM;
import com.iflytek.sparkchain.core.LLMCallbacks;
import com.iflytek.sparkchain.core.LLMConfig;
import com.iflytek.sparkchain.core.LLMError;
import com.iflytek.sparkchain.core.LLMEvent;
import com.iflytek.sparkchain.core.LLMFactory;
import com.iflytek.sparkchain.core.LLMOutput;
import com.iflytek.sparkchain.core.LLMResult;
import com.iflytek.sparkchain.core.Memory;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ImageUnderstanding extends AppCompatActivity implements View.OnClickListener{
    private static final String TAG = "AEE_imageUnderstanding";
    private static final int AUDIO_FILE_SELECT_CODE = 1024;

    private TextView btn_imgInput,btn_arunStart,btn_stop,tv_area;

    private EditText ed_textInput;

    private ImageView selected_image;

    private String imagePath = null;
    private int token = 0;
    LLM llm;
    /*********
     * 图片理解结果监听回调
     * ***********/
    private LLMCallbacks mLLMCallbacksListener = new LLMCallbacks() {
        @Override
        public void onLLMResult(LLMResult llmResult, Object usrContext) {
            if(token == (int)usrContext){
                Log.d(TAG,"onLLMResult\n");
                String content = llmResult.getContent();//获取大模型返回的结果信息
                Log.e(TAG,"onLLMResult:" + content);
                int status = llmResult.getStatus();
                if(content != null) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tv_area.append(content);
                            toend();
                        }
                    });
                }
                if(status == 2){//2表示大模型结果返回完成
                    int completionTokens = llmResult.getCompletionTokens();
                    int promptTokens = llmResult.getPromptTokens();//
                    int totalTokens = llmResult.getTotalTokens();
                    Log.e(TAG,"completionTokens:" + completionTokens + "promptTokens:" + promptTokens + "totalTokens:" + totalTokens);
                }
            }
        }

        @Override
        public void onLLMEvent(LLMEvent llmEvent, Object o) {

        }

        @Override
        public void onLLMError(LLMError error, Object o) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tv_area.append("错误:" + " err:" + error.getErrCode() + " errDesc:" + error.getErrMsg() + "\n");
                }
            });
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_understanding);
        Window window = this.getWindow(); window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
        initView();
    }

    private void initView() {
        selected_image = findViewById(R.id.selected_image);
        btn_imgInput = findViewById(R.id.image_understanding_imginput);
        btn_arunStart = findViewById(R.id.image_understanding_arun_start_btn);
        btn_stop = findViewById(R.id.image_understanding_arun_stop_btn);
        tv_area = findViewById(R.id.image_understanding_area);
        tv_area.setMovementMethod(new ScrollingMovementMethod());
        ed_textInput = findViewById(R.id.image_understanding_input_text);
        btn_imgInput.setOnClickListener(this);
        btn_arunStart.setOnClickListener(this);
        btn_stop.setOnClickListener(this);
        ed_textInput.setBackgroundTintList(ColorStateList.valueOf(Color.parseColor("#333333")));
        setLLMConfig();
    }

    private void showInfo(String text){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tv_area.setText(text);
            }
        });
    }
    /***************
     * 配置文本交互LLM，注册结果监听回调
     * ******************/
    private void setLLMConfig(){
        LLMConfig llmConfig = LLMConfig.builder()
                .domain("image");
        Memory window_memory = Memory.windowMemory(5);
        llm = LLMFactory.imageUnderstanding(llmConfig,window_memory);
        llm.registerLLMCallbacks(mLLMCallbacksListener);
    }
    /***************
     * 开始交互，异步
     * ****************/
    private void startChat() {
        if(llm == null){
            Log.e(TAG,"startChat failed,please setLLMConfig before!");
            return;
        }
        String usrInputText = ed_textInput.getText().toString();
        Log.d(TAG,"用户输入：" + usrInputText);
        if(usrInputText.length() >= 1 && imagePath != null) {
            tv_area.append("\n输入:\n    " + usrInputText + "\n");
            token++;
            Log.e(TAG, "SparkChain imagePath:\n" + imagePath);
            int ret = -1;
            if (imagePath != null) {
                llm.clearHistory();//重新传图片前，需要清空memory，因为memory带有上一次图片的信息
                ret = llm.arun(usrInputText, readFileByBytes(imagePath), token);//首轮会话需要带上图片信息
            } else {
                ret = llm.arun(usrInputText, token);//多轮会话可以不用携带图片信息，SDK会在历史会话中自动拼接图片信息。
            }
            if (ret != 0) {
                Log.e(TAG, "SparkChain failed:\n" + ret);
            }

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ed_textInput.setText("");
                    tv_area.append("输出:\n    ");
                    imagePath = null;//第一轮会话后清空图片信息
                }
            });
        }else{
            Toast.makeText(this, "图片和问题均不能为空", Toast.LENGTH_SHORT).show();
        }
    }

    /***************
     * 开始交互，同步。仅展示如何使用，demo中未使用此方式
     * ****************/
    private void syncStartChat(){
        if(llm == null){
            Log.e(TAG,"startChat failed,please setLLMConfig before!");
            return;
        }
        String usrInputText = ed_textInput.getText().toString();
        Log.d(TAG,"用户输入：" + usrInputText);
        if(usrInputText.length() >= 1)
            tv_area.append("\n输入:\n    " + usrInputText  + "\n");
        token++;
        Log.e(TAG,"SparkChain imagePath:\n" + imagePath);
        int ret = -1;
        LLMOutput syncOutput = null;
        if(imagePath!=null){
            llm.clearHistory();//重新传图片前，需要清空memory，因为memory带有上一次图片的信息
            syncOutput = llm.run(usrInputText,readFileByBytes(imagePath));
        } else{
            syncOutput = llm.run(usrInputText);
        }
        if(syncOutput.getErrCode() == 0) {
            Log.i(TAG, "同步调用：" +  syncOutput.getRole() + ":" + syncOutput.getContent());
            String results = syncOutput.getContent();
            showInfo(results);
        }else {
            String results = "同步调用：" +  "errCode" + syncOutput.getErrCode() + " errMsg:" + syncOutput.getErrMsg();
            showInfo(results);
            Log.e(TAG, results);
        }
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ed_textInput.setText("");
                tv_area.append("输出:\n    ");
                imagePath = null;
            }
        });
    }

    /***************
     * 取消交互
     * ****************/
    private void stop(){
        if(llm == null){
            Log.e(TAG,"startChat failed,please setLLMConfig before!");
            return;
        }
        llm.stop();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.image_understanding_imginput:
                showFileChooser();
                break;
            case R.id.image_understanding_arun_start_btn:
                startChat();
//                syncStartChat();
                break;
            case R.id.image_understanding_arun_stop_btn:
                stop();
                break;
        }
    }
    /***************
     * 调用文本管理器，让用户选择要传入的图片
     * ****************/
    private void showFileChooser() {
        Log.d(TAG,"showFileChooser");
        //调用系统文件管理器
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        //设置文件格式
        intent.setType("*/*");
        startActivityForResult(intent, AUDIO_FILE_SELECT_CODE);
    }
    /***************
     * 监听用户选择的图片，获取图片所在的路径
     * ****************/
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        switch (requestCode) {
            case AUDIO_FILE_SELECT_CODE:
                if (data != null) {
                    Uri uri = data.getData();
                    String path = GetFilePathFromUri.getFileAbsolutePath(this, uri);
                    imagePath = path;
                }
                if(imagePath == null){
                    showInfo("选择图片失败");
                }else{
                    selected_image.setImageURI(Uri.parse(imagePath));
                    showInfo("图片已设置完成");
                }
                break;
        }
        super.onActivityResult(requestCode, resultCode, data);

        Log.d(TAG,"imagePath = " + imagePath);
    }
    /***************
     * 把对应路径的图片转换成二进制流
     * ****************/
    private byte[] readFileByBytes(String fileName) {
        FileInputStream in = null;
        try {
            in = new FileInputStream(fileName);
        } catch (FileNotFoundException e) {
            Log.e("AEE", "readFileByBytes:" + e.toString());
        }
        byte[] bytes = null;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            byte[] temp = new byte[1024];
            int size = 0;
            while ((size = in.read(temp)) != -1) {
                out.write(temp, 0, size);
            }
            in.close();
            bytes = out.toByteArray();
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bytes;
    }

    public void toend(){
        int scrollAmount = tv_area.getLayout().getLineTop(tv_area.getLineCount()) - tv_area.getHeight();
        if (scrollAmount > 0) {
            tv_area.scrollTo(0, scrollAmount+10);
        }
    }
}
